﻿using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Xml;
using repliback4.Net.Protocol;
using repliback4.Net.Protocol.Messages;
using repliback4.Net.Protocol.States;

using repliback4.XmlStreaming;
using Tools.Debugging;
using repliback4.Net.Identification;
using repliback4.Net.Protocol.States.ServerSide;
using repliback4.Misc.Queue;

namespace repliback4.Net.ServerSide {

    public class ClientInfo {
        public Rid Rid;
        public Boolean Authenticated = false;
        public Boolean Encrypted = false;

        public override string ToString ( ) {
            return String.Format( "", this.Rid, this.Authenticated, this.Encrypted );
        }
    }

    //public class QueueManager<T> {
    //    public Queue<T> incomingQueue = new Queue<T>( );
    //    public Queue<T> outgoingQueue = new Queue<T>( );

    //}

    //public class InfoQueryQueueManager : QueueManager<InfoQuery> {

    //}
    public enum QueueDirection { In, Out };
    public class Client:IDisposable {

        internal ServerConnection serverConection;
        protected Server server;
        public Server Server { get { return this.server; } }

        protected QueueManager<InfoQuery, QueueDirection> queue;
        public QueueManager<InfoQuery, QueueDirection> Queue { get { return this.queue; } }


        protected MessageParser parser = new MessageParser( );
        public MessageParser Parser { get { return this.parser; } }

        protected ServerStateManager stateManager;
        internal XmlStreamWriter writer;

        internal ClientInfo ClientInfo = new ClientInfo( );


        public Client ( Server server, TcpClient connection ) {
            Logger.Created( this );
            this.server = server;

            this.queue = new QueueManager<InfoQuery, QueueDirection>( );
           
            this.queue.CreateQueue( QueueDirection.In );
            this.queue.AddOnItemInQueueHandler( QueueDirection.In, new AutoQueue<InfoQuery>.OnItemInQueueHandler( this.queueIn_OnDeque ) );

            this.queue.CreateQueue( QueueDirection.Out );
            this.queue.AddOnItemInQueueHandler( QueueDirection.Out, new AutoQueue<InfoQuery>.OnItemInQueueHandler( this.queueOut_OnDeque ) );

            this.serverConection = new ServerConnection( this, connection );
            this.serverConection.OnReceive += new AsyncConnection.OnReceiveHandler( this.serverConection_OnReceive );
            this.serverConection.OnDisconnect += new AsyncConnection.OnDisconnectHandler( this.serverConection_OnDisconnect );
            this.serverConection.OnRemoteDisconnect += new AsyncConnection.OnRemoteDisconnectHandler( this.serverConection_OnRemoteDisconnect );
            this.serverConection.OnConnect += new AsyncConnection.OnConnectHandler( this.serverConection_OnConnect );

            this.parser.OnXmlDeclaration += new MessageParser.OnXmlDeclarationHandler( this.parser_OnXmlDeclaration );
            this.parser.OnStreamError += new MessageParser.OnStreamErrorHandler( this.parser_OnStreamError );
            this.parser.OnStreamBegin += new MessageParser.OnStreamBeginHandler( this.parser_OnStreamBegin );
            this.parser.OnStreamEnd += new MessageParser.OnStreamEndHandler( parser_OnStreamEnd );
            this.parser.OnTag += new MessageParser.OnTagHandler( this.parser_OnTag );

            this.stateManager = new ServerStateManager( this );
            this.stateManager.CurrentState = new DisconnecedState( this.stateManager );

            this.writer = new XmlStreamWriter( );

            this.serverConection.Start( );
        }

        ~Client ( ) {
            Logger.Debug( this, "destructing" );
           // this.Finalize( );
        }

        #region :: Queue Event handlers

        protected void queueOut_OnDeque ( InfoQuery iq ) {

        }
        protected void queueIn_OnDeque ( InfoQuery iq ) {

            this.Server.Queue.Enque( ServerQueueDirection.FromClient, new QueueQueryItem( ) { Query = iq, Client = this } );
        }

        #endregion

        #region :: Parser Event handlers ::

        void parser_OnXmlDeclaration ( XmlNode xmlDeclaration ) {
            Logger.Debug( this, "received XML declaration" );
            this.stateManager.CurrentState.ExecuteXmlDeclaration( xmlDeclaration );
        }


        void parser_OnStreamEnd ( BaseStream stream ) {
            Logger.Debug( this, "end stream" );
            this.stateManager.CurrentState.ExecuteStreamEnd( stream );

        }

        void parser_OnTag ( ReplibackTag stanza ) {
            Logger.Debug( this, "received tag {0}", stanza.GetType( ).FullName );
            try {
                this.stateManager.CurrentState.ExecuteTag( stanza );
            } catch ( Exception e ) {
                Logger.Debug( this, "ExecuteTag Exception in [{0}] - [{1}]", this.stateManager.CurrentState.GetType( ).FullName, e.Message );
            }
        }

        void parser_OnStreamBegin ( BaseStream stream ) {
            Logger.Debug( this, "staring stream {0}", stream.GetType( ).FullName );
            this.stateManager.CurrentState.ExecuteStreamStart( stream );
        }


        void parser_OnStreamError ( ) {
            Logger.Debug( this, " stream error{0}" );
            this.stateManager.CurrentState.ExecuteStreamError( );
            //this.serverConection.Send( "" );
        }

        #endregion
        #region :: Connection Event handlers ::

        public void serverConection_OnReceive ( Response response ) {
            //Logger.Debug( this, String.Format( "serverConection_OnReceive: {0}", response.Data ) );
            try {
                this.parser.Parse( response.Data );
            } catch ( XmlException e ) {
                Logger.Debug( this, String.Format( "xml parser exception: [{0}] : {1}", e.Message, new int[] { e.LineNumber, e.LinePosition } ) );
            } catch ( Exception e ) {
                Logger.Debug( this, String.Format( "general parsing exception: [{0}]", e.Message ) );
            }
        }
        public void serverConection_OnDisconnect ( DisconnectData data ) {
            Logger.Debug( this, "fired serverConection_OnDisconnect" );
            this.Server.Disconnected( this );
        }
        public void serverConection_OnRemoteDisconnect ( RemoteDisconnectData data ) {
            Logger.Debug( this, "fired serverConection_OnRemoteDisconnect" );
        }
        public void serverConection_OnConnect ( ConnectData data ) {
            Logger.Debug( this, "fired serverConection_OnConnect, binding xmlwriter to socket" );
            this.writer.Writer = data.connection.StreamWriter;

        }

        #endregion


        public void Start ( ) {
            //throw new NotImplementedException( );
        }

        #region IDisposable Members

        public void Dispose ( ) {
            this.queue.Dispose( );
            this.serverConection.Dispose( );
            //this.stateManager.Dispose( );
            //this.writer.Dispose( );
            
        }

        #endregion

       
    }
}
